package com.zim.ta_service.base.error;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.zim.ta_service.base.exception.AuthException;
import com.zim.ta_service.base.exception.BizMsgException;
import com.zim.ta_service.base.model.vo.R;
import com.zim.ta_service.support.ExceptionUtil;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.MyBatisSystemException;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
 * author: alabimofa
 * date: 2021/10/9 17:21
 */
@Order(-120)
@Slf4j
@ControllerAdvice
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public class GlobalExceptionHandler {

    /**
     * 请求参数缺失异常
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public R missParamException(MissingServletRequestParameterException e) {
        String parameterType = e.getParameterType();
        String parameterName = e.getParameterName();
        return R.error(StrUtil.format(">>> 缺少请求的参数{}，类型为{}", parameterName, parameterType));
    }

    /**
     * 拦截参数格式传递异常
     */
    @ExceptionHandler({HttpMessageNotReadableException.class})
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public R httpMessageNotReadable(HttpMessageNotReadableException e) {
        return R.error(StrUtil.format("参数格式错误:{}", e.getMessage()));
    }

    /**
     * 拦截不支持媒体类型异常
     */
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public R httpMediaTypeNotSupport(HttpMediaTypeNotSupportedException e) {
        return R.error(StrUtil.format("参数格式错误:{}", e.getMessage()));
    }

    /**
     * 拦截请求方法的异常
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public R methodNotSupport(HttpServletRequest request) {
        if (ServletUtil.isPostMethod(request)) {
            return R.error("请求方法应为GET");
        }
        return R.error("请求方法应为POST");
    }

    /**
     * 拦截资源找不到的运行时异常
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ResponseBody
    public R notFound(NoHandlerFoundException e) {
        return R.error(StrUtil.format("URL不存在:{}", e.getRequestURL()));
    }

    /**
     * 拦截参数校验错误异常,JSON传参
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public R methodArgumentNotValidException(MethodArgumentNotValidException e) {
        return R.error(getArgNotValidMessage(e.getBindingResult()));
    }

    /**
     * 拦截参数校验错误异常
     */
    @ExceptionHandler(BindException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public R paramError(BindException e) {
        return R.error(getArgNotValidMessage(e.getBindingResult()));
    }

    /**
     * 拦截认证失败异常
     */
    @ExceptionHandler(AuthException.class)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ResponseBody
    public R authFail(AuthException e) {
        return R.error(e.getErrorMsg());
    }

    /**
     * 拦截业务异常
     */
    @ExceptionHandler(BizMsgException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public R businessError(BizMsgException e) {
        return R.error(e.getErrorMsg());
    }

    /**
     * 拦截mybatis数据库操作的异常
     */
    @ExceptionHandler(MyBatisSystemException.class)
    @ResponseBody
    public R persistenceException(MyBatisSystemException e) {
		log.error(e.getMessage(), e);
        return R.error(StrUtil.format("Mybatis异常:{}", e.getMessage()), ExceptionUtil.getExceptionClassName(e));
    }

    /**
     * 拦截未知的运行时异常
     */
    @ExceptionHandler(Throwable.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public R serverError(Throwable e) {
		log.error(e.getMessage(), e);
        return R.error(StrUtil.format("服务器异常:{}", e.getMessage()), ExceptionUtil.getExceptionClassName(e));
    }

    /**
     * 获取请求参数不正确的提示信息
     * 多个信息 仅返回第一个
     */
    private String getArgNotValidMessage(BindingResult bindingResult) {
        String errorMsg = "参数校验不通过";
        if (bindingResult == null || CollUtil.isEmpty(bindingResult.getAllErrors())) {
            return errorMsg;
        }

        List<ObjectError> allErrorInfos = bindingResult.getAllErrors();
        for (ObjectError error : allErrorInfos) {
            errorMsg = error.getDefaultMessage();
            break;
        }
        return errorMsg;
    }
}
